﻿using Nasty.Core.Entity.Flow;
using Nasty.Core.Entity.System;
using Nasty.Core.LoadParams;
using Nasty.Core.Model;
using Nasty.Core.Registry;
using Nasty.Core.Repository;
using Nasty.Core.Session;
using Nasty.Core.SuperExtension;
using Nasty.FlowModule.Areas.Flow.Model;
using Nasty.FlowModule.Areas.Flow.Params;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Principal;
using System.Text;
using System.Threading.Tasks;
using static Dm.net.buffer.ByteArrayBuffer;
using static System.Collections.Specialized.BitVector32;

namespace Nasty.FlowModule.Flow
{
    public interface IFlowRepository : IRepository<Nasty.Core.Entity.Flow.Flow>
    {
        public List<FlowLog> GetFlowLogs(string id);

        public Nasty.Core.Entity.Flow.Flow GetFlow(string id);

        public ResultData<Nasty.Core.Entity.Flow.Flow> SaveFlow(Nasty.FlowModule.Areas.Flow.Model.FlowModel model);

        public ResultData<FlowNextInfoModel> GetFlowNextInfo(GetFlowNextInfoParams @params);

        public ResultData<string> AgreeFlowNode(AgreeFlowNodeParams @params);

        public ResultData<string> RejectFlowNode(RejectFlowNodeParams @params);

        public ResultData<string> RevokeFlow(string id);

        public PageData<Nasty.Core.Entity.Flow.Flow> GetFlowPage(GetFlowPageParams @params);

        public ResultData<string> DelegateFlowNode(DelegateFlowNodeParams @params);
    }

    public class FlowRepository : SqlRepository<Nasty.Core.Entity.Flow.Flow>, IFlowRepository
    {
        public FlowRepository(SqlSugarClient db) : base(db)
        {
            //db.CodeFirst.InitTables(typeof(Nasty.Core.Entity.Flow.Flow));
            //db.CodeFirst.InitTables(typeof(Nasty.Core.Entity.Flow.FlowNode));
        }

        public List<FlowLog> GetFlowLogs(string id)
        {
            return Db.Queryable<FlowLog>()
                   .Where(log => log.FlowId == id && log.NodeCode != "Start" && log.NodeCode != "End")
                   .IncludesAllFirstLayer()
                   .OrderBy(log => log.CreateTime, OrderByType.Asc)
                   .ToList();
        }

        public ResultData<string> RevokeFlow(string id)
        {
            var result = new ResultData<string>();
            try
            {  
                var flow = Db.Queryable<Nasty.Core.Entity.Flow.Flow>().InSingle(id);
                var flowAction = ServiceLocator.GetByName<IAutofacFlowAction>(flow.FlowAction);
                var revokeResult = flowAction.Revoke(flow);

                //删除流程
                Db.Delete(flow);

                //删除流程日志
                Db.Deleteable<FlowLog>().Where((t) => t.FlowId == id).ExecuteCommand();

                //删除流程相关通知消息
                Db.Deleteable<UserMessage>().Where((t) => t.ThirdId.StartsWith(id)).ExecuteCommand();

                result.IsSuccess = true;
                return result;
            }
            catch (Exception ex)
            {
                result.IsSuccess = false;
                result.Message = ex.Message;
                return result;
            }
        }

        public PageData<Nasty.Core.Entity.Flow.Flow> GetFlowPage(GetFlowPageParams @params)
        {
            int totalPage = 0;
            int total = 0;
            var pageData = new PageData<Nasty.Core.Entity.Flow.Flow>();

            var _SQLExpress = Db.Queryable<Nasty.Core.Entity.Flow.Flow>().IncludesAllFirstLayer();

            if (!string.IsNullOrEmpty(@params.Name)) _SQLExpress.Where((t) => t.Name.Contains(@params.Name));

            _SQLExpress = _SQLExpress.OrderBy((t) => t.CreateTime, OrderByType.Desc);

            var data = _SQLExpress.ToPageList(@params.Current, @params.PageSize, ref total, ref totalPage);

            pageData.Total = total;
            pageData.TotalPage = totalPage;
            pageData.Data = data;

            pageData.Current = @params.Current;
            pageData.PageSize = @params.PageSize;
            return pageData;
        }

        public ResultData<FlowNextInfoModel> GetFlowNextInfo(GetFlowNextInfoParams @params)
        {
            var result = new ResultData<FlowNextInfoModel>();

            try
            {
                var flow = Db.Queryable<Nasty.Core.Entity.Flow.Flow>().IncludesAllFirstLayer().InSingle(@params.Id);
                if (flow == null)
                {
                    result.IsSuccess = false;
                    result.Message = "流程不能为空";
                    return result;
                }

                var nextNodes = Db.Queryable<Nasty.Core.Entity.Flow.FlowNode>()
                    .Where(node => node.FlowId == flow.Id && SqlFunc.JsonArrayAny(node.PreNodeCodes, flow.CurrentNodeCode))
                    .IncludesAllFirstLayer()
                    .ToList();

                var currentNode = Db.Queryable<Nasty.Core.Entity.Flow.FlowNode>()
                    .Where(node => node.FlowId == flow.Id && node.Code == flow.CurrentNodeCode)
                    .IncludesAllFirstLayer()
                    .First();

                var action = flow.FlowAction;

                var flowAction = ServiceLocator.GetByName<IAutofacFlowAction>(flow.FlowAction);
                var branchResult = flowAction.Branch(flow, currentNode, nextNodes);
                if (branchResult.Data == null) throw new Exception("获取下一个节点错误");

                var candidates = new List<FlowCandidateMaxModel>();

                foreach (var nextNode in branchResult.Data)
                {
                    if (nextNode.CandidateMode == CandidateMode.Action)
                    {
                        var candidate = flowAction.Candidate(flow, currentNode, nextNode);
                        var candidateData = new FlowCandidateMaxModel
                        {
                            NodeId = candidate.Data.NodeId,
                            NodeCode = candidate.Data.NodeCode,
                            Users = candidate.Data.Users,
                            CandidateMode = CandidateMode.Action
                        };

                        candidates.Add(candidateData);
                    }
                    else
                    {
                        var preUserIds = nextNode.PreUserIds ?? new List<string>();
                        var users = Db.Queryable<User>().Where((t) => preUserIds.Contains(t.Id)).ToList();

                        var candidateData = new FlowCandidateMaxModel
                        {
                            NodeId = nextNode.Id,
                            NodeCode = nextNode.Code,
                            CandidateMode = nextNode.CandidateMode,
                            Users = users,
                            RoleIds = nextNode.RoleIds,
                            DepartmentIds = nextNode.DepartmentIds,
                        };

                        candidates.Add(candidateData);
                    }
                
                }


                //如果除本次同意外 审批人数量与已审批人数量不匹配表示为会签中且不是最后一个会签人，那么无需选择下一步节点
                var isCanNext = (currentNode.ApproverIds.Count == 0 ? 0 : currentNode.ApproverIds.Count - 1) == (currentNode.CompleteApproverIds?.Count ?? 0);
                var data = new FlowNextInfoModel
                {
                    Flow = flow,
                    NextNodes = branchResult.Data,
                    Candidates = candidates,
                    IsCanNext = isCanNext,
                };

                result.Data = data;
                result.IsSuccess = true;
                return result;
            }
            catch (Exception ex)
            {
                result.IsSuccess = false;
                result.Message = ex.Message;
                return result;
            }
        }

        public ResultData<string> DelegateFlowNode(DelegateFlowNodeParams @params)
        {
            var result = new ResultData<string>();

            try
            {
                Db.BeginTran();
                var flow = Db.Queryable<Nasty.Core.Entity.Flow.Flow>().IncludesAllFirstLayer().InSingle(@params.Id);
                if (flow == null)
                {
                    result.IsSuccess = false;
                    result.Message = "流程不能为空";
                    return result;
                }

                var currentNode = Db.Queryable<Nasty.Core.Entity.Flow.FlowNode>()
                    .Where(node => node.FlowId == flow.Id && node.Id == flow.CurrentNodeId)
                    .IncludesAllFirstLayer()
                    .First();

                if (currentNode == null)
                {
                    result.IsSuccess = false;
                    result.Message = "当前节点不存在";
                    return result;
                }

                var userInfo = AppSession.CurrentUser.Value;
                var user = Db.Queryable<User>().InSingle(userInfo.Id);

                if (!currentNode.ApproverIds.Contains(user.Id))
                {
                    result.IsSuccess = false;
                    result.Message = "非节点审批人无法委托";
                    return result;
                }

                if (user.Id == @params.UserId)
                {
                    result.IsSuccess = false;
                    result.Message = "无法委派自己审批";
                    return result;
                }

                var target = Db.Queryable<User>().InSingle(@params.UserId);


                var flowAction = ServiceLocator.GetByName<IAutofacFlowAction>(flow.FlowAction);
                var rejectResult = flowAction.Delegate(flow, currentNode, user, target);

                //删除自己
                currentNode.ApproverIds.Remove(user.Id);
                //添加委托人
                currentNode.ApproverIds.Add(@params.UserId);
                Db.Update(currentNode);

                flow.CurrentApproverIds = currentNode.ApproverIds;
                Db.Update(flow);

                //通知,日志
                var remark = $"委派至{target.Name}审批\n{@params.Remark}";
                WriteFlowLog(flow, currentNode, user, FlowLogAction.Delegate, remark);
                DeleteFlowMessage(flow.Id, user.Id);
                AddFlowMessage(flow, @params.UserId);
               

                Db.CommitTran();
                result.IsSuccess = true;
                return result;
            }
            catch (Exception ex)
            {
                Db.RollbackTran();
                result.IsSuccess = false;
                result.Message = ex.Message;
                return result;
            }
        }

        public ResultData<string> AgreeFlowNode(AgreeFlowNodeParams @params)
        {
            var result = new ResultData<string>();

            try
            {
                Db.BeginTran();
                var flow = Db.Queryable<Nasty.Core.Entity.Flow.Flow>().InSingle(@params.Id);
                if (flow == null)
                {
                    result.IsSuccess = false;
                    result.Message = "流程不能为空";
                    return result;
                }

                var currentNode = Db.Queryable<FlowNode>()
                    .Where(node => node.FlowId == flow.Id && node.Id == flow.CurrentNodeId)
                    .IncludesAllFirstLayer()
                    .First();

                if (currentNode == null)
                {
                    result.IsSuccess = false;
                    result.Message = "当前节点不存在";
                    return result;
                }


                var userInfo = AppSession.CurrentUser.Value;
                var user = Db.Queryable<User>().InSingle(userInfo.Id);


                //更新已完成审批人字段
                if (currentNode.CompleteApproverIds == null) currentNode.CompleteApproverIds = new List<string>();
                currentNode.CompleteApproverIds.Add(userInfo.Id);
                Db.Update(currentNode);

                //如果大于等于审批人数量表示会签或者单签通过，可以进行下一步节点
                var isCanNext = currentNode.CompleteApproverIds.Count >= currentNode.ApproverIds.Count;


                FlowNode nextNode = null;
                if (!string.IsNullOrEmpty(@params.NodeId)) nextNode = Db.Queryable<FlowNode>().IncludesAllFirstLayer().InSingle(@params.NodeId);
                if (nextNode == null && isCanNext)
                {
                    result.IsSuccess = false;
                    result.Message = "下一个节点不存在";
                    return result;
                }


                var action = flow.FlowAction;
                var flowAction = ServiceLocator.GetByName<IAutofacFlowAction>(flow.FlowAction);

                if (nextNode.Code == "End")
                {
                    var endResult = flowAction.End(flow, nextNode);
                }
                else
                {
                    var agreeResult = flowAction.Agree(flow, currentNode, nextNode, user);
                }
       


                if (!isCanNext)
                {
                    WriteFlowLog(flow, currentNode, user, FlowLogAction.Agree, @params.Remark);
                    Db.CommitTran();
                    result.IsSuccess = true;
                    return result;
                }

                //重置已审批人字段（这里可能是驳回过的中间节点，所以默认清空下一个节点已审批人）
                nextNode.CompleteApproverIds = new List<string>();
                //更新流程节点审批人
                nextNode.ApproverIds = @params.UserIds;
                Db.Update(nextNode);

                flow.CurrentNodeId = nextNode.Id;
                flow.CurrentNode = nextNode; //方便给End赋值
                flow.CurrentApproverIds = @params.UserIds;
                Db.Update(flow);

                //通知,日志
                WriteFlowLog(flow, currentNode, user, FlowLogAction.Agree, @params.Remark);
                DeleteFlowMessage(flow.Id, user.Id);
                AddFlowMessage(flow);
    
                Db.CommitTran();
                result.IsSuccess = true;
                return result;
            }
            catch (Exception ex)
            {
                Db.RollbackTran();
                result.IsSuccess = false;
                result.Message = ex.Message;
                return result;
            }
        }

        /// <summary>
        /// 驳回流程节点
        /// </summary>
        /// <param name="params"></param>
        /// <returns></returns>
        public ResultData<string> RejectFlowNode(RejectFlowNodeParams @params)
        {
            var result = new ResultData<string>();

            try
            {
                Db.BeginTran();
                var flow = Db.Queryable<Nasty.Core.Entity.Flow.Flow>().InSingle(@params.Id);
                if (flow == null)
                {
                    result.IsSuccess = false;
                    result.Message = "流程不能为空";
                    return result;
                }

                var currentNode = Db.Queryable<Nasty.Core.Entity.Flow.FlowNode>()
                    .Where(node => node.FlowId == flow.Id && node.Id == flow.CurrentNodeId)
                    .IncludesAllFirstLayer()
                    .First();



                if (currentNode == null)
                {
                    result.IsSuccess = false;
                    result.Message = "当前节点不存在";
                    return result;
                }

                var rejectNode = Db.Queryable<Nasty.Core.Entity.Flow.FlowNode>()
                                 .Where(node => node.FlowId == flow.Id && node.Code == currentNode.RejectNodeCode)
                                 .IncludesAllFirstLayer()
                                 .First();

                if (rejectNode == null)
                {
                    result.IsSuccess = false;
                    result.Message = "驳回节点不存在";
                    return result;
                }

                var flowAction = ServiceLocator.GetByName<IAutofacFlowAction>(flow.FlowAction);

                var rejectResult = flowAction.Reject(flow, currentNode, rejectNode);

                //重置已审批人字段
                rejectNode.CompleteApproverIds = new List<string>();
                Db.Update(rejectNode);

                ////驳回 节点 审批人 有待确定
                flow.CurrentNodeId = rejectNode.Id;
                flow.CurrentNode = rejectNode;
                flow.CurrentApproverIds = rejectNode.ApproverIds; //驳回节点的审批人
                Db.Update(flow);

                var userInfo = AppSession.CurrentUser.Value;
                var user = Db.Queryable<User>().InSingle(userInfo.Id);

                //通知,日志
                WriteFlowLog(flow, currentNode, user, FlowLogAction.Reject, @params.Remark);
                DeleteFlowMessage(flow.Id, user.Id);
                AddFlowMessage(flow);
         
                Db.CommitTran();
                result.IsSuccess = true;
                return result;
            }
            catch (Exception ex)
            {
                Db.RollbackTran();
                result.IsSuccess = false;
                result.Message = ex.Message;
                return result;
            }

        }

        public Nasty.Core.Entity.Flow.Flow GetFlow(string id)
        {
            var data =  this.Db.Queryable<Nasty.Core.Entity.Flow.Flow>().IncludesAllFirstLayer().InSingle(id);
            return data;
        }

        public ResultData<string> WriteFlowLog(Nasty.Core.Entity.Flow.Flow flow, FlowNode node, User user, FlowLogAction action, string remark)
        {
            var result = new ResultData<string>();
            try
            {
                var flowLog = new FlowLog
                {
                    FlowId = flow.Id,
                    NodeId = node.Id,
                    NodeName = node.Name,
                    NodeCode = node.Code,
                    UserId = user.Id,
                    UserName = user.Name,
                    Action = action,
                    Remark = remark,
                };

                Db.Add(flowLog);
                result.IsSuccess = true;
                return result;
            }
            catch (Exception ex)
            {
                result.IsSuccess = false;
                result.Message = ex.Message;
                return result;
            }
        }

        public void DeleteFlowMessage(string flowId, string userId)
        {
            var thirdId = $"{flowId}{userId}";
            Db.Deleteable<UserMessage>().Where((t) => t.ThirdId == thirdId).ExecuteCommand();
        }

        public ResultData<string> AddFlowMessage(Nasty.Core.Entity.Flow.Flow flow)
        {
            var result = new ResultData<string>();
            try
            {
                foreach (var userId in flow.CurrentApproverIds)
                {
                    AddFlowMessage(flow, userId);
                }

                result.IsSuccess = true;
                return result;
            }
            catch (Exception ex)
            {
                result.IsSuccess = false;
                result.Message = ex.Message;
                return result;
            }
        }

        public void AddFlowMessage(Nasty.Core.Entity.Flow.Flow flow, string userId)
        {
            var msg = new UserMessage
            {
                UserId = userId,
                ThirdId = $"{flow.Id}{userId}",
                Title = "您有新的审批任务",
                Description = $"流程名称：{flow.Name}，当前节点：{flow.CurrentNode.Name}",
                MessageType = MessageType.Flow,
                Extend = $"{{\"FlowId\":\"{flow.Id}\",\"NodeId\":\"{flow.CurrentNodeId}\",\"Action\":\"{flow.NoticeAction}\"}}"
            };


            Db.Add(msg);
        }


        public ResultData<Nasty.Core.Entity.Flow.Flow> SaveFlow(Nasty.FlowModule.Areas.Flow.Model.FlowModel model)
        {
            var result = new ResultData<Nasty.Core.Entity.Flow.Flow>();
            try
            {
                Db.BeginTran();
                if (string.IsNullOrEmpty(model.FlowConfigId))
                {
                    if (string.IsNullOrEmpty(model.FlowConfigCode)) throw new Exception("流程配置Id或Code不能为空");

                    var config = Db.Queryable<Nasty.Core.Entity.Flow.FlowConfig>()
                        .Where(t => t.Code == model.FlowConfigCode)
                        .First();
                    if (config == null) throw new Exception("流程配置不存在");
                    if (!config.IsPublish) throw new Exception("流程配置未发布");
                    model.FlowConfigId = config.Id;
                }

                var data = Db.Save<Nasty.Core.Entity.Flow.Flow>(model);
                Db.CommitTran();
                result.IsSuccess = true;
                return result;
            }
            catch (Exception ex)
            {
                Db.RollbackTran();
                result.IsSuccess = false;
                result.Message = ex.Message;
                return result;
            }
        }


    }
}
